package com.itita.ww2.game.alliance;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;
import com.itita.ww2.core.MessageHandler;
import com.itita.ww2.core.R;
import com.itita.ww2.core.ServerService;
import com.itita.ww2.enums.PushCode;
import com.itita.ww2.game.alliance.enums.AlEventType;
import com.itita.ww2.game.alliance.event.AllianceEventHandler;
import com.itita.ww2.model.user.UserProfile;
import com.itita.ww2.puredb.model.Alliance;
import com.itita.ww2.puredb.model.AllianceMember;
import com.itita.ww2.redis.RedisUtil;
import com.itita.ww2.redis.cache.Table;
import com.itita.ww2.redis.core.Redis;
import com.smartfoxserver.v2.entities.User;
import com.smartfoxserver.v2.entities.data.ISFSObject;

public class AllianceManager {
	private static final Logger logger = LoggerFactory.getLogger(AllianceManager.class);
	private static AllianceManager instance = new AllianceManager();

	public static final AllianceManager getInstance() {
		return instance;
	}

	private LoadingCache<String, Alliance> m_AllianceMap; // aid - alliance
	private ConcurrentHashMap<String, ConcurrentHashMap<Integer, User>> m_AllianceOnlineUserMap; // 联盟在线成员
	//
	private Table cacheAlWorldMapTable;

	public AllianceManager() {
		// @formatter:off
		this.m_AllianceMap = CacheBuilder.newBuilder()
				.maximumSize(1000)
				.recordStats()
				.removalListener(new RemovalListener<String, Alliance>() {
					@Override
					public void onRemoval(RemovalNotification<String, Alliance> arg0) {
						logger.info("alliance has removed. aid:{} name:{}",arg0.getKey(),arg0.getValue());
					}
				})
				.initialCapacity(20)
				.build(new CacheLoader<String, Alliance>() {
					@Override
					public Alliance load(String arg0) throws Exception {
						if(StringUtils.isNotBlank(arg0)){
							return Alliance.getByAid(arg0);
						}
						return null;
					}
				});
		// @formatter:on
		m_AllianceOnlineUserMap = new ConcurrentHashMap<>();
	}

	public void init() {
		initRedis();
		loadAlData();
		AllianceEventHandler.registerEvents(this);
	}

	public void addAlliance(Alliance alliance) {
		this.m_AllianceMap.put(alliance.getAid(), alliance);
	}

	private void loadAlData() {
		List<Alliance> alliances = Alliance.getAllAlliance();
		for (Alliance alliance : alliances) {
			this.m_AllianceMap.put(alliance.getAid(), alliance);
		}
	}

	private void initRedis() {
		Redis redis = ServerService.getInstance().getRedis();
		// 
		cacheAlWorldMapTable = redis
				.setTable(R.ServerRedisKey.ALLIANCE_WMDATA, RedisUtil.genericKey("aid"), false, true).withSort(
						"base_occTimes", "base_defTimes", "city_occTimes", "city_defTimes", "base_count", "city_count").init();
	}

	public final Table getCacheAlWorldMapTable() {
		return cacheAlWorldMapTable;
	}

	public Collection<Alliance> getAllAlliance() {
		return this.m_AllianceMap.asMap().values();
	}

	public String getAidByUid(int uid) {
		AllianceMember member = AllianceMember.getByUid(uid);
		if (member == null) {
			return "";
		}
		return member.getAid();
	}

	public void addAllianceUser(UserProfile userProfile) {
		User user = userProfile.getSFSUser();
		if (userProfile.isJoinAlliance() && (user != null) && user.isConnected()) {
			ConcurrentHashMap<Integer, User> alUsers = m_AllianceOnlineUserMap.get(userProfile.getAid());
			if (alUsers == null) {
				alUsers = new ConcurrentHashMap<>();
			}
			alUsers.put(userProfile.getUid(), user);
			m_AllianceOnlineUserMap.put(userProfile.getAid(), alUsers);
		}
	}

	public void removeAllianceUser(UserProfile userProfile) {
		if (userProfile.isJoinAlliance()) {
			ConcurrentHashMap<Integer, User> alUsers = m_AllianceOnlineUserMap.get(userProfile.getAid());
			if (alUsers != null) {
				alUsers.remove(userProfile.getUid());
			}
		}
	}

	public void removeAllUserByAid(String aid) {
		if (!StringUtils.isBlank(aid)) {
			m_AllianceOnlineUserMap.remove(aid);
		}
	}

	public List<User> getAllianceOnlineMember(String aid) {
		ConcurrentHashMap<Integer, User> ret = m_AllianceOnlineUserMap.get(aid);
		if (ret != null) {
			return new ArrayList<>(ret.values());
		}
		return null;
	}

	public Alliance getAlliance(String aid) {
		if (StringUtils.isBlank(aid)) {
			logger.warn("Alliance aid is null or empty!", aid);
			return null;
		}
		Alliance ret = null;
		try {
			ret = m_AllianceMap.get(aid);
		} catch (Exception e) {
			logger.warn("Alliance {} not found!", aid);
			// e.printStackTrace();
		}
		return ret;
	}

	public void removeAlliance(String aid) {
		removeAllUserByAid(aid);
		m_AllianceMap.invalidate(aid);
	}

	/**
	 * 给一个联盟的所有在线成员推消息
	 */
	public void pushAllianceMsg(String aid, PushCode code, ISFSObject pushObj) {
		ConcurrentHashMap<Integer, User> ret = m_AllianceOnlineUserMap.get(aid);
		if (ret != null) {
			MessageHandler.getInstance().pushAl(code, pushObj, new ArrayList<>(ret.values()));
		}
	}

	public void pushAllianceWMEvent(String aid, PushCode code, ISFSObject pushObj, User exceptUser) {
		ConcurrentHashMap<Integer, User> ret = m_AllianceOnlineUserMap.get(aid);
		if (ret != null) {
			List<User> users = ret.values().stream().filter(p -> p != exceptUser).collect(Collectors.toList());
			MessageHandler.getInstance().pushWMEvent(code, pushObj, users);
		}
	}

	public void pushAllianceNotice(String aid, PushCode code, ISFSObject pushObj) {
		ConcurrentHashMap<Integer, User> ret = m_AllianceOnlineUserMap.get(aid);
		if (ret != null) {
			MessageHandler.getInstance().pushAlNotice(code, pushObj, new ArrayList<>(ret.values()));
		}
	}

	public void pushAllianceEvent(String aid, AlEventType eventType, ISFSObject pushObj) {
		ConcurrentHashMap<Integer, User> ret = m_AllianceOnlineUserMap.get(aid);
		if (ret != null) {
			MessageHandler.getInstance().pushAlEvent(
					PushCode.valueOf(PushCode.AL_EVT.getValue() + eventType.ordinal() + 1), pushObj,
					new ArrayList<>(ret.values()));
		}
	}
}
